package com.ftg.learn.util;

import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.util.CollectionUtils;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 通用树结构生成类
 *
 * @author maohb
 * @version 1.0
 * @date 2023/3/21 8:00
 */
@AllArgsConstructor
@SuppressWarnings("unchecked")
public class TreeUtil<S, T> {

    private String idName;
    private String parentIdName;
    private String childrenName;
    private List<S> sourceList;
    private Class<T> targetClass;

    public TreeUtil(List<S> sourceList, Class<T> targetClass) {
        this.idName = "id";
        this.parentIdName = "parentId";
        this.childrenName = "children";
        this.sourceList = sourceList;
        this.targetClass = targetClass;
    }

    public T getTreeNode(BigInteger parentId) throws Exception {
        T parent = null;
        for (S source : sourceList) {
            BeanWrapper sourceWrapper = PropertyAccessorFactory.forBeanPropertyAccess(source);
            if (parentId.equals(sourceWrapper.getPropertyValue(idName))) {
                parent = targetClass.newInstance();
                BeanUtils.copyProperties(source, parent);
                break;
            }
        }
        if (Objects.nonNull(parent)) {
            sourceList = new CopyOnWriteArrayList<>(sourceList);
            return buildTree(parent);
        }
        return null;
    }

    public void listId(T root, List<BigInteger> idList) {
        BeanWrapper rootWrapper = PropertyAccessorFactory.forBeanPropertyAccess(root);
        idList.add((BigInteger) rootWrapper.getPropertyValue(idName));
        List<T> children = (List<T>) rootWrapper.getPropertyValue(childrenName);
        if (Objects.requireNonNull(children).size() > 0) {
            for (T child : children) {
                listId(child, idList);
            }
        }
    }

    private T buildTree(T parent) throws Exception {
        BeanWrapper parentWrapper = PropertyAccessorFactory.forBeanPropertyAccess(parent);
        BigInteger parentId = (BigInteger) parentWrapper.getPropertyValue(idName);
        List<T> children = (List<T>) parentWrapper.getPropertyValue(childrenName);
        if (Objects.isNull(children)) {
            parentWrapper.setPropertyValue(childrenName, new ArrayList<>());
            children = (List<T>) parentWrapper.getPropertyValue(childrenName);
        }
        findChildren(parentWrapper, parentId, children);
        if (!CollectionUtils.isEmpty(children)) {
            for (T child : children) {
                buildTree(child);
            }
        }
        return parent;
    }

    private void findChildren(BeanWrapper parentWrapper, BigInteger parentId, List<T> children) throws Exception {
        if (!CollectionUtils.isEmpty(sourceList)) {
            for (S source : sourceList) {
                BeanWrapper sourceWrapper = PropertyAccessorFactory.forBeanPropertyAccess(source);
                if (parentId.equals(sourceWrapper.getPropertyValue(parentIdName))) {
                    T child = targetClass.newInstance();
                    BeanUtils.copyProperties(source, child);
                    Objects.requireNonNull(children).add(child);
                    sourceList.remove(source);
                }
            }
        }
    }

}
